#pragma once

#include "common.h"
#include "LfsrRegister.h"
#include "LfsrConfiguration.h"
#include "Lfsr.h"
#include "ResultStorage.h"
#include "Random.h"
#include "Algebra.h"
#include <string>

namespace LfsrTests
{
	template<class Category = left_operator_tag, int regSize = 2, int factorsSize = 2>
	class BruteIterator : public SimpleIterator<typename Lfsr<Category, regSize, factorsSize>::Configuration>
	{
	public:
		typedef Lfsr<Category, regSize, factorsSize> Generator;
		typedef typename Generator::Configuration Configuration;
		typedef typename Generator::Factorset Factorset;
		typedef typename Generator::State State;

		BruteIterator(const Factorset &last, int triesCount = 1)
			: _last(last), 
			_random(), 
			_triesCount(triesCount), 
			_tryIndex(triesCount - 1)
		{
			_currentConfiguration = new Configuration(State(0), _last);
		}
		
		~BruteIterator()
		{
			delete _currentConfiguration;
		}

		bool moveNext()
		{
			_currentConfiguration->reg.fillAtRandom(&_random);
			_tryIndex++;
			if(_tryIndex == _triesCount)
			{
				_currentConfiguration->factors.increment();
				_tryIndex = 0;
				return _currentConfiguration->factors != _last;
			}
			return true;
		}

		Configuration current() const
		{
			return *_currentConfiguration;
		}
	private:
		Factorset _last;
		Configuration *_currentConfiguration;
		Random _random;
		int _triesCount;
		int _tryIndex;
	};

	template <int regSize = 2, int factorsSize = 2>
	class ConfigurationFactorsComparer
	{
	private:
		LfsrRegisterComparer<factorsSize> _fcmp;

	public:
		bool operator () (
			const LfsrConfiguration<regSize, factorsSize> &a, 
			const LfsrConfiguration<regSize, factorsSize> &b) const
		{
			return _fcmp(a.factors, b.factors);
		}
	};

	template <int regSize = 2, int factorsSize = 2>
	class ConfigurationRegistersComparer
	{
	private:
		LfsrRegisterComparer<factorsSize> _fcmp;

	public:
		bool operator () (
			const LfsrConfiguration<regSize, factorsSize> &a, 
			const LfsrConfiguration<regSize, factorsSize> &b) const
		{
			return _fcmp(a.reg, b.reg);
		}
	};

	void defaultTest(IAlgebra *algebra);

	bool generate(IAlgebra *algebra, int seqLength, std::string fileName);

}